

#include "Texture.h"

#include "SDL/SDL.h"
#include "SDL_image/SDL_image.h"

#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>

#define TEXTURE_DEBUG

/**********************************************************/ 
/*                    Texture class                       */
/**********************************************************/ 
  

Texture::Texture() :
TextureName(0)
{}

Texture::~Texture()
{
	if (m_image)
	{
		::SDL_FreeSurface(m_image);
	}
}


void Texture::Load(std::string inFilename)
{
	mipmapmode = BUILD_MIPMAPS;
	mode = TEXTURE_MODULATE;

#ifdef TEXTURE_DEBUG
	printf("loading texture: %s\n",inFilename.c_str());
#endif

	::glEnable(GL_TEXTURE_2D);
	
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	
	m_image = ::IMG_Load(inFilename.c_str());
	
	if(m_image == NULL) 
	{
		printf("IMG_Load error loading image %s - %s\n", inFilename.c_str(), ::SDL_GetError());
		exit(1);
	}
	else
	{
		int mWidth = m_image->w;
		int mHeight = m_image->h;
		int mDepth = m_image->format->BitsPerPixel;
#ifdef TEXTURE_DEBUG
		printf("image dims, height, width: %d %d\n",mHeight,mWidth);
#endif	

		glGenTextures(1, &TextureName);

		glBindTexture(GL_TEXTURE_2D, TextureName);
		
		switch (mipmapmode)
		{
			
			case NO_MIPMAPS:
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				break;
				
			case BUILD_MIPMAPS:
				glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);				
				break;
				
		}
														
		GLenum internal_format = GL_RGBA8;		
		// could be GL_RGB5_A1, if you don't need 8 bit alpha and want to save VRAM
		GLenum image_format, image_type;
		switch(mDepth)
		{
			case 32:
				image_format = GL_BGRA_EXT;
				image_type = GL_UNSIGNED_INT_8_8_8_8_REV;
				break;
				
			case 24:
				image_format = GL_RGB;
				image_type = GL_UNSIGNED_BYTE;
				break;
				
			case 16:
				image_format = GL_RGB5_A1;
				image_type = GL_UNSIGNED_SHORT_5_5_5_1;
				break;
				
			default:
				image_format = GL_LUMINANCE;
				image_type = GL_UNSIGNED_BYTE;
				break;
		}
		
		switch (mipmapmode) 
		{
			
			case NO_MIPMAPS:
				glTexImage2D(GL_TEXTURE_2D, 
							 0, 
							 internal_format, 
							 mWidth, mHeight, 
							 0, 
							 image_format, 
							 image_type, 
							 m_image->pixels);
				break;
				
			case BUILD_MIPMAPS:
				gluBuild2DMipmaps(GL_TEXTURE_2D,
								  internal_format,
								  m_image->w, m_image->h,
								  image_format,
								  image_type,
								  m_image->pixels);
				break;
				
		}
	
		GLenum gl_error = ::glGetError();
		if(gl_error != GL_NO_ERROR)
			fprintf(stderr, "OpenGL error: %d\n", (int)gl_error);
		
		
	}
	
}

void Texture::Apply() const
{
	switch (mode)
	{
		case TEXTURE_MODULATE: glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); break;
		case TEXTURE_REPLACE:  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);  break;
		case TEXTURE_ADD:      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD    );  break;
		case TEXTURE_COMBINE:  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);  break;
		case TEXTURE_BLEND:    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND  );  break;
		case TEXTURE_DECAL:    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL  );  break;
	}
	
	if(TextureName != 0)
	{
		glBindTexture(GL_TEXTURE_2D, TextureName);
	}
}


Vec4 Texture::getRGBatUV(float u, float v)
{
	int x = int(u * getWidth());
	int y = int(v * getHeight());

	Uint32 pixel = getPixel(x,y);
	
	Uint8 r, g, b;
	SDL_GetRGB(pixel, m_image->format, &r, &g, &b);
	
	float R = float(r)/255.f;
	float G = float(g)/255.f;
	float B = float(b)/255.f;
	
	return Vec4(R,G,B);
}

int Texture::getHeight()
{
	return m_image->h;
}

int Texture::getWidth()
{
	return m_image->w;
}


// Return the pixel value at (x, y)
Uint32 Texture::getPixel(int x, int y)
{
	SDL_LockSurface(m_image);

	int bpp = m_image->format->BytesPerPixel;
	 
	/* Here p is the address to the pixel we want to retrieve */
	Uint8 *p = (Uint8 *)m_image->pixels + y * m_image->pitch + x * bpp;
 
	switch (bpp)
	{
		 case 1:
			 return *p;
	
		 case 2:
			 return *(Uint16 *)p;
	 
		 case 3:
			 if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
					 return p[0] << 16 | p[1] << 8 | p[2];
			 else
					 return p[0] | p[1] << 8 | p[2] << 16;
	 
		 case 4:
			 return *(Uint32 *)p;
	 
		 default:
			 return 0;       /* shouldn't happen, but avoids warnings */
	 } 
	
	SDL_UnlockSurface(m_image);
}


GLuint Texture::GetTextureName() const
{
	return TextureName;	
}

